/*
 * FileTransferTcpServerSession.cpp
 *
 *  Created on: Sep 12, 2012
 *      Author: shawnmao
 */

#include "FileTransferTcpServerSession.h"
#include "FileBuffer.h"
#include "FileDriver.h"

FileTransferTcpServerSession::FileTransferTcpServerSession(string local_addr,
							 uint16_t local_port)
:TcpServerSession(local_addr, local_port),
 m_transfer_state(TCP_FILE_TRANSFER_IDLE)
{
}

void FileTransferTcpServerSession::execute_srv_protocol(int* ptr_conn_sock)
{
#ifdef DEBUG_FILE_TRANSFER_TCP_SERVER_SESSION
	cout<<"Thread:"<<pthread_self()<<": connected socket is: "<<*ptr_conn_sock<<endl;
#endif

	int ret_val;
	string send_str, recv_str;
	uint16_t recv_len;
	ssize_t send_size, recv_size;
	struct sockaddr_in remote_addr;
	socklen_t remote_addr_len = sizeof(struct sockaddr);
	string remote_addr_str;

	/*get the address and port of remote client host*/
	ret_val = getpeername(*ptr_conn_sock, (struct sockaddr*)&remote_addr, &remote_addr_len);
	if(0 != ret_val) {
		cout<<"Fail to get remote address of this connection, "
				"the failure reason is: "<<errno<<endl;
		close(*ptr_conn_sock);
		return;
	}
	remote_addr_str.append(inet_ntoa(remote_addr.sin_addr));

	/*send "Session is connected." to client host*/
	send_str.append("Session is connected.");
	ret_val = send_ctrl_msg(send_str, ptr_conn_sock);
	if(-1 == ret_val) {
		return;
	}

	/*receive "OK." from client host*/
	ret_val = recv_msg_type(ptr_conn_sock);
	if((-1 == ret_val) || (-2 == ret_val) ) {
		return;
	}
	if(TCP_CONTROL_MSG != ret_val) {
		close(*ptr_conn_sock);
		return;
	}
	ret_val = recv_msg_len(ptr_conn_sock);
	if((-1 == ret_val) || (-2 == ret_val) ) {
		return;
	}
	recv_len = ret_val;
	ret_val = recv_ctrl_msg(&recv_str, recv_len, ptr_conn_sock);
	if((-1 == ret_val) || (-2 == ret_val) ) {
		return;
	}
#ifdef DEBUG_FILE_TRANSFER_TCP_SERVER_SESSION
		cout<<"Thread:"<<pthread_self()<<": Msg received from client (Address: "
				<<remote_addr_str<<", Port: "<<ntohs(remote_addr.sin_port)<<") is: "
				<<recv_str<<endl;
#endif
	ret_val = recv_str.compare("OK");
	if(0 == ret_val) {
		cout<<"Establish a connection with client session (Address: "
					<<remote_addr_str<<", Port: "<<ntohs(remote_addr.sin_port)<<")"<<endl;
	}
	else {
		cout<<"Tcp server session has not receive the correct "
				"response from client session, the response from server "
				"session is: "<<recv_str<<endl;
		close(*ptr_conn_sock);
		return;
	}

	/*receive file name from client host*/
	recv_str.clear();
	ret_val = recv_msg_type(ptr_conn_sock);
	if((-1 == ret_val) || (-2 == ret_val) ) {
		return;
	}
	if(TCP_CONTROL_MSG != ret_val) {
		close(*ptr_conn_sock);
		return;
	}
	ret_val = recv_msg_len(ptr_conn_sock);
	if((-1 == ret_val) || (-2 == ret_val) ) {
		return;
	}
	recv_len = ret_val;
	ret_val = recv_ctrl_msg(&recv_str, recv_len, ptr_conn_sock);
	if((-1 == ret_val) || (-2 == ret_val) ) {
		return;
	}
#ifdef DEBUG_FILE_TRANSFER_TCP_SERVER_SESSION
	cout<<"Thread:"<<pthread_self()<<": Msg received from client (Address: "
			<<remote_addr_str<<", Port: "<<ntohs(remote_addr.sin_port)<<") is: "
			<<recv_str<<endl;
#endif

	/*receive file content from client host*/
	ret_val = recv_msg_type(ptr_conn_sock);
	if((-1 == ret_val) || (-2 == ret_val) ) {
		return;
	}
	if(TCP_FILE_TRANSFER != ret_val) {
		close(*ptr_conn_sock);
		return;
	}
	ret_val = recv_file("jinchang_recv.jpg", ptr_conn_sock);

	close(*ptr_conn_sock);
}

int FileTransferTcpServerSession::send_ctrl_msg(string send_msg, int* ptr_conn_sock)
{
	int ret_val;
	TcpMessageType msg_type = TCP_CONTROL_MSG;

	/*send control message type*/
	ret_val = send(*ptr_conn_sock, (void*)&msg_type, sizeof(TcpMessageType), 0);
	if(-1 == ret_val) {
		cout<<"Fail to send control message type to client host"<<endl;
		close(*ptr_conn_sock);
		return -1;
	}
	else if(ret_val < sizeof(TcpMessageType)) {
		cout<<"Wrong control message type has been sent to client host."<<endl;
		close(*ptr_conn_sock);
		return -1;
	}

	/*send control message length*/
	uint16_t msg_len = send_msg.length();
	ret_val = send(*ptr_conn_sock, (void*)&msg_len, 2, 0);
	if(-1 == ret_val) {
		cout<<"Fail to send control message length to client host"<<endl;
		close(*ptr_conn_sock);
		return -1;
	}
	else if(ret_val < 2) {
		cout<<"Wrong control message length has been sent to client host."<<endl;
		close(*ptr_conn_sock);
		return -1;
	}

	/*send control message content*/
	ret_val = send(*ptr_conn_sock, (void*)send_msg.c_str(), send_msg.length(), 0);
	if(-1 == ret_val) {
		cout<<"Fail to send control message content to client host"<<endl;
		close(*ptr_conn_sock);
		return -1;
	}
	else if(ret_val < send_msg.length()) {
		cout<<"Incomplete control message content has been sent to client host."<<endl;
		close(*ptr_conn_sock);
		return -1;
	}

	return 0;
}

int FileTransferTcpServerSession::recv_msg_type(int* ptr_conn_sock)
{
	int ret_val;
	TcpMessageType msg_type;

	ret_val = recv(*ptr_conn_sock, (void*)&msg_type, sizeof(TcpMessageType), 0);
	if(-1 == ret_val) {
		cout<<"Fail to receive message type form client host."<<endl;
		close(*ptr_conn_sock);
		return -1;
	}
	else if(0 == ret_val) {
		cout<<"Client has shutdown the connection."<<endl;
		close(*ptr_conn_sock);
		return -2;
	}
	else if(ret_val < sizeof(TcpMessageType)) {
		cout<<"Wrong message type has been received from client host"<<endl;
		close(*ptr_conn_sock);
		return -1;
	}

	return msg_type;
}

int FileTransferTcpServerSession::recv_msg_len(int* ptr_conn_sock)
{
	int ret_val;
	uint16_t msg_len;

	ret_val = recv(*ptr_conn_sock, (void*)&msg_len, 2, 0);char buf_for_recv[msg_len + 1];
	if(-1 == ret_val) {
		cout<<"Fail to receive message length from client host."<<endl;
		close(*ptr_conn_sock);
		return -1;
	}
	else if(0 == ret_val) {
		cout<<"Client has shutdown the connection."<<endl;
		close(*ptr_conn_sock);
		return -2;
	}

	return msg_len;
}

int FileTransferTcpServerSession::recv_file_len(int* ptr_conn_sock, uint64_t& file_len)
{
	int ret_val;
//	uint64_t msg_len;

	ret_val = recv(*ptr_conn_sock, (void*)&file_len, sizeof(file_len), 0);
	if(-1 == ret_val) {
		cout<<"Fail to receive message length from client host."<<endl;
		close(*ptr_conn_sock);
		return -1;
	}
	else if(0 == ret_val) {
		cout<<"Client has shutdown the connection."<<endl;
		close(*ptr_conn_sock);
		return -2;
	}

	return 0;
}

int FileTransferTcpServerSession::recv_ctrl_msg(string* recv_msg, ssize_t msg_len, int* ptr_conn_sock)
{
	int ret_val;
	ssize_t recv_size = 0;
	char buf_for_recv[msg_len + 1];

	do {
		bzero(buf_for_recv, sizeof(buf_for_recv));
		ssize_t ret_val = recv(*ptr_conn_sock, (void*)buf_for_recv, (msg_len - recv_size), 0);
		if(-1 == ret_val) {
			cout<<"Fail to receive control message from client, "
				  "the failure reason is: "<<errno<<endl;
			close(*ptr_conn_sock);
			return -1;
		}
		else if(0 == ret_val) {
			cout<<"Client has shutdown the connection."<<endl;
			close(*ptr_conn_sock);
			return -2;
		}

		(*recv_msg).append(buf_for_recv);
		recv_size += ret_val;
	}while(recv_size < msg_len);

	return recv_size;
}

int FileTransferTcpServerSession::recv_file(string file_name, int* ptr_conn_sock)
{
	int ret_val;
	uint64_t file_len, recv_size;
	FILE* p_file = NULL;
	uint64_t buffer_size = 0;
	FileBuffer file_buffer;
	fstream file_str;

	ret_val = recv_file_len(ptr_conn_sock, file_len);
	if((-1 == ret_val) || (-2 == ret_val) ) {
		return -1;
	}
//	file_len = ret_val;

	cout<<"The file length is: "<<file_len<<endl;

	recv_size = 0;
	/*p_file = fopen(file_name.c_str(), "w+");
	if(NULL == p_file) {
		cout<<"Fail to open file with reason: "<<errno<<endl;
		return -1;
	}*/
	file_str.open(file_name.c_str(), ios_base::out | ios_base::trunc | ios_base::binary);
	if(false == file_str.good()) {
		cout<<"Fail to open file: "<<file_name<<endl;
		return -1;
	}

	do {
		buffer_size = (FILE_TRANSFER_BUFFER_SZIE < (file_len - recv_size)) ?
				FILE_TRANSFER_BUFFER_SZIE : (file_len - recv_size);
		file_buffer.buffer_free();
		file_buffer.buffer_new(buffer_size);
		ret_val = recv(*ptr_conn_sock, (void*)(file_buffer.buffer_get()), buffer_size, 0);
		if(-1 == ret_val) {
			cout<<"Fail to receive file content from client, "
				  "the failure reason is: "<<errno<<endl;
//			close(*ptr_conn_sock);
			return -1;
		}
		else if(0 == ret_val) {
			cout<<"Client has shutdown the connection."<<endl;
//			close(*ptr_conn_sock);
			return -2;
		}

		/*if(0 != file_driver.file_write(file_buffer.buffer_get(), ret_val)) {
			close(*ptr_conn_sock);
			return -1;
		}*/

//		cout<<"Buffer received from client is: "<<endl<<(char*)(file_buffer.buffer_get())<<endl;
		cout<<"The length of Buffer received is: "<<ret_val<<endl;

//		ret_val = fwrite(file_buffer.buffer_get(), 1, file_buffer.buffer_get_size(), p_file);
		file_str.write((const char*)(file_buffer.buffer_get()), file_buffer.buffer_get_size());
		if(true == file_str.fail()) {
			cout<<"Fail to write buffer to file: "<<file_name<<endl;
		}

		/*if(file_buffer.buffer_get_size() > ret_val) {
			cout<<"Fail to write buffer to file."<<endl;
		}*/

		recv_size += ret_val;
		cout<<"The size of received buffer is: "<<recv_size<<endl;
	}while(recv_size < file_len);

	file_buffer.buffer_free();
//	file_driver.file_close();
	fclose(p_file);

	return 0;
}
